package demo;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Javen
 * <p>
 * 公平锁  ReentrantLock(true)
 * 非公平锁 synchronized ReentrantLock
 * 可重入锁 synchronized ReentrantLock
 * <p>
 * <p>
 * Synchronized与ReentrantLock区别总结
 * 
 * Synchronized 它是 java 语言的关键字，是原生语法层面的互斥，需要 JVM 实现。
 * 会在同步块的前后分别形成monitorenter和monitorexit这个两个字节码指令。
 * 在执行monitorenter指令时，首先要尝试获取对象锁。如果这个对象没被锁定，
 * 或者当前线程已经拥有了那个对象锁，把锁的计算器加1，相应的，
 * 在执行monitorexit指令时会将锁计算器就减1，当计算器为0时，锁就被释放了
 *
 * 在JavaSE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的 偏向锁 和 轻量级锁 以及其它各种优化之后变得在某些情况下并不是那么重了。
 * synchronized的底层实现主要依靠 Lock-Free 的队列，基本思路是 自旋后阻塞，竞争切换后继续竞争锁，稍微牺牲了公平性，但获得了高吞吐量。
 * 在线程冲突较少的情况下，可以获得和CAS类似的性能；而线程冲突严重的情况下，性能远高于CAS
 *
 * ReentrantLock是 JDK1.5引入的 等待可中断 lock.lockInterruptibly()
 * 可实现公平锁
 * 锁绑定多个条件
 *
 * ReenTrantLock的实现是一种自旋锁，通过循环调用CAS操作来实现加锁
 *
 * 缺点：
 * ABA 问题
 * 循环时间长开销大
 * 只能保证一个共享变量的原子操作
 * 
 * https://blog.csdn.net/zxd8080666/article/details/83214089
 *
 * http://www.blogjava.net/zhanglongsr/articles/356782.html
 *
 *
 */
@Slf4j
public class ReenterLockDemo {
    public static void main(String[] args) {
        Person person = new Person();
        new Thread(() -> person.setSyn(), "T1").start();

        new Thread(person::setSyn, "T2").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        log.info("lock ......");

        new Thread(person, "T3").start();
        new Thread(person, "T4").start();
    }
}

@Slf4j
class Person implements Runnable {
    Lock lock = new ReentrantLock();

    public void set() {
        try {
            lock.lock();
            // 只要成双成对就可以
            lock.lock();
            log.info("{} invoked set...", Thread.currentThread().getId());
            get();
        } finally {
            lock.unlock();
            lock.unlock();
        }
    }

    public void get() {
        try {
            lock.lock();
            log.info("{} invoked get...", Thread.currentThread().getId());
        } finally {
            lock.unlock();
        }
    }


    public synchronized void setSyn() {
        log.info("{} invoked setSyn...", Thread.currentThread().getId());
        getSyn();
    }

    public synchronized void getSyn() {
        log.info("{} invoked getSyn...", Thread.currentThread().getId());
    }


    @Override
    public void run() {
        set();
    }
}

